﻿using System;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Reflection;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using MSPE.Public.FOSS.AutoStatusPlus.Common;
using MSPE.Public.FOSS.AutoStatusPlus.TimerJob;

namespace MSPE.Public.FOSS.AutoStatusPlus.EventHandler
{
    public class TSEventHandlerInstaller : SPFeatureReceiver
    {
        #region Private / Protected
        private EventEntry _eventLog;
        private SPWeb _pwaWeb;
        private const string _handlerEventName = "AutoStatusPlus TSEventHandler";
        private const string _timerEventName = "AutoStatusPlus StatusingTimerJob";

        #region Constants
        private const int _beginSecond = 0;
        private const int _endSecond = 59;
        private const string _eventLogName = "Application";
        private const string _eventSourceName = "AutoStatusPlus TSEventHandler";
        #endregion // Constants
        #endregion // Private / Protected

        #region Methods
        #region Elevated Privs
        private void DeleteDbObjects()
        {
            using (SPSite _pwaSite = _pwaWeb.Site)
            {
                // Couldn't create SPSite
                if (_pwaSite == null) throw new Exception("Unable to initialize PWA Site Collection Object.");

                using (SPWeb _elevatedWeb = _pwaSite.RootWeb)
                {
                    try
                    {
                        // Couldn't create SPWeb
                        if (_elevatedWeb == null) throw new Exception("Unable to initialize PWA Web Object.");

                        Settings _webSettings = Settings.LoadSettings(_elevatedWeb);

                        string _connString = Utility.BuildConnectionString(_webSettings.DbUseIntegratedAuth, _webSettings.DbServer, _webSettings.DbName, _webSettings.DbUser, _webSettings.DbPassword);

                        // Retrieve embedded script
                        string _sqlScript = EmbeddedResource.GetString(GlobalConstants.SqlDropScriptName);

                        // Create SQL Connection and Command Objects
                        SqlConnection _sqlConn = new SqlConnection(_connString);
                        _sqlConn.Open();

                        if (_sqlConn.State != ConnectionState.Open) throw new Exception("Unable to establish a connection to SQL using the provided information.");

                        // Execute the drop script
                        SqlCommand _sqlCmd = new SqlCommand(_sqlScript, _sqlConn);
                        _sqlCmd.CommandType = CommandType.Text;

                        _sqlCmd.ExecuteNonQuery();
                        _sqlConn.Close();
                        _sqlCmd.Dispose();
                        _sqlConn.Dispose();
                    } // End try

                    finally
                    {
                        if (_elevatedWeb != null) _elevatedWeb.Dispose();
                    } // End finally
                }
            } // End using
        } // DeleteDbObjects()

        private void RegisterEventHandler()
        {
            using (SPWeb _elevatedWeb = SPContext.Current.Web)
            {
                Settings _webSettings = Settings.LoadSettings(_elevatedWeb);

                //todo: alter to check if someone already has handler position 0
                // Declarations
                Assembly _currentAssembly;
                EventsSvc.Events _eventsSvc = null;
                EventsSvc.EventHandlersDataSet _eventHandlersDs = null;
                EventsSvc.EventHandlersDataSet _eventHandlersUpdateDs = null;
                WebServiceManager _webRefMgr;

                if (!Enum.IsDefined(typeof(TimesheetEventEnum), _webSettings.Event))
                    throw new Exception(_webSettings.Event + " is not a valid TimesheetEvent.");

                try
                {
                    // Initializations
                    _webRefMgr = new WebServiceManager { PwaUrl = _elevatedWeb.Url };
                    _eventsSvc = _webRefMgr.GetWebRef(WebServiceTypeEnum.Events) as EventsSvc.Events;
                    _eventHandlersUpdateDs = new EventsSvc.EventHandlersDataSet();
                    _currentAssembly = Assembly.GetExecutingAssembly();

                    if (_eventsSvc == null)
                        throw new Exception("Unable to initialize web reference!");

                    // Get a list of event handlers
                    _eventHandlersDs = _eventsSvc.ReadEventHandlerAssociationsForEvent((EventsSvc.PSEventID)_webSettings.Event);

                    // Make sure the event handler doesn't already exist
                    foreach (EventsSvc.EventHandlersDataSet.EventHandlersRow _eventHandler in _eventHandlersDs.EventHandlers)
                    {
                        if (_eventHandler.Name == GlobalConstants.EventHandlerName)
                            throw new InvalidOperationException("Event handler already exists.");
                    } // End foreach

                    string _eventHandlerName = GlobalConstants.EventHandlerName + " - " + _webSettings.Event;

                    // Set up the event handler association
                    _eventHandlersUpdateDs.EventHandlers.AddEventHandlersRow(Guid.NewGuid(),
                                                                            _eventHandlerName,
                                                                             _currentAssembly.FullName,
                                                                             GlobalConstants.EventHandlerClass,
                                                                             (int)_webSettings.Event,
                                                                             GlobalConstants.EventHandlerDescription,
                                                                             GlobalConstants.EventHandlerOrder);


                    // Associate the event handler
                    _eventsSvc.CreateEventHandlerAssociations(_eventHandlersUpdateDs);
                } // End try

                finally
                {
                    if (_eventsSvc != null) _eventsSvc.Dispose();
                    if (_eventHandlersUpdateDs != null) _eventHandlersUpdateDs.Dispose();
                    if (_eventHandlersDs != null) _eventHandlersDs.Dispose();
                } // End finally
            } // End using
        } // RegisterEventHandler()

        private void UnregisterEventHandler()
        {
            using (SPWeb _elevatedWeb = SPContext.Current.Web)
            {
                Settings _webSettings = Settings.LoadSettings(_elevatedWeb);

                // Declarations
                EventsSvc.Events _eventsSvc = null;
                EventsSvc.EventHandlersDataSet _eventHandlersDs = null;
                Guid[] _eventHandlerGuids;
                WebServiceManager _webRefMgr;

                if (!Enum.IsDefined(typeof(TimesheetEventEnum), _webSettings.Event))
                    throw new Exception(_webSettings.Event + " is not a valid TimesheetEvent.");

                try
                {
                    // Initializations
                    _eventHandlerGuids = new Guid[1];
                    _webRefMgr = new WebServiceManager { PwaUrl = _elevatedWeb.Url };
                    _eventsSvc = _webRefMgr.GetWebRef(WebServiceTypeEnum.Events) as EventsSvc.Events;

                    if (_eventsSvc == null)
                        throw new Exception("Unable to initialize web reference!");

                    string _eventHandlerName = GlobalConstants.EventHandlerName + " - " + _webSettings.Event;

                    // Get a list of event handlers
                    _eventHandlersDs = _eventsSvc.ReadEventHandlerAssociationsForEvent((EventsSvc.PSEventID)_webSettings.Event);

                    foreach (EventsSvc.EventHandlersDataSet.EventHandlersRow _eventHandler in _eventHandlersDs.EventHandlers)
                    {
                        if (_eventHandler.Name == _eventHandlerName || _eventHandler.Name.Contains(GlobalConstants.EventHandlerName))
                        {
                            _eventHandlerGuids[0] = _eventHandler.EventHandlerUid;
                            _eventsSvc.DeleteEventHandlerAssociations(_eventHandlerGuids);
                        } // End if
                    } // End foreach
                } // End try

                finally
                {
                    if (_eventsSvc != null) _eventsSvc.Dispose();
                    if (_eventHandlersDs != null) _eventHandlersDs.Dispose();
                } // End finally
            }
        }
        #endregion Elevated Privs
        #endregion // Methods

        #region Event Handlers
        /// <summary>
        /// Occurs after a Feature is activated.
        /// </summary>
        /// <param name="properties">An 
        /// <see cref="T:Microsoft.SharePoint.SPFeatureReceiverProperties">
        /// </see> object that represents the properties of the event.</param>
        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            int _jobDeleteCount = 0;
            _eventLog = new EventEntry(_eventSourceName);
            Settings _webSettings;
            _pwaWeb = properties.Feature.Parent as SPWeb;

            try
            {
                // Couldn't create SPWeb
                if (_pwaWeb == null) throw new Exception("Could not access the feature's parent Site.");

                // Not a PWA Site
                if (_pwaWeb.WebTemplateId != GlobalConstants.PwaSiteTemplateId) throw new Exception("This feature may only be activated on Project Web Access sites.");

                // Site not configured
                if (!Utility.AllKeysExist(_pwaWeb)) throw new Exception("You must run stsadm -o AspSetPwaSite against this site before activating this feature.");

                _webSettings = Settings.LoadSettings(_pwaWeb);

                // Create Timer Job
                _jobDeleteCount = Utility.CreateTimerJob(_pwaWeb, _webSettings);

                // Register Event Handler
                //Utility.RegisterEventHandler(_pwaWeb, _webSettings.Event);
                SPSecurity.CodeToRunElevated _elevatedRegisterEventHandler = new SPSecurity.CodeToRunElevated(RegisterEventHandler);
                SPSecurity.RunWithElevatedPrivileges(_elevatedRegisterEventHandler);

                _eventLog.WriteEvent("Successfully registered event handler during feature activation.", EventLogEntryType.Information);
            } // End try

            catch (Exception ex)
            {
                _eventLog.WriteEvent("Error encountered while activating TSEventHandler feature.\n\n" + ex.ToString());
                throw;
            } // End catch

            finally
            {
                if (_pwaWeb != null) _pwaWeb.Dispose();
                _eventLog.Dispose();
            } // End finally
        } // FeatureActivated()

        /// <summary>
        /// Occurs before a Feature is deactivated.
        /// </summary>
        /// <param name="properties">An 
        /// <see cref="T:Microsoft.SharePoint.SPFeatureReceiverProperties">
        /// </see> object that represents the properties of the event.</param>
        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
            int _jobDeleteCount = 0;
            _eventLog = new EventEntry(_eventSourceName);

            _pwaWeb = properties.Feature.Parent as SPWeb;

            // Couldn't create SPWeb
            if (_pwaWeb == null) throw new Exception("Could not access the feature's parent Site.");

            Settings _webSettings = Settings.LoadSettings(_pwaWeb);

            try
            {
                #region Delete DB Objects
                SPSecurity.CodeToRunElevated _elevatedSql = new SPSecurity.CodeToRunElevated(DeleteDbObjects);
                SPSecurity.RunWithElevatedPrivileges(_elevatedSql);
                #endregion // Delete DB Objects

                // Delete Timer Job
                _jobDeleteCount = Utility.DeleteTimerJob(_pwaWeb);

                if (_jobDeleteCount > 0)
                    _eventLog.WriteEvent("Successfully deleted " + _jobDeleteCount + " statusing timer jobs associated with the feature's parent" + " Site during feature deactivation.", EventLogEntryType.Information);

                else
                    _eventLog.WriteEvent("No statusing timer jobs associated with the feature's" + " parent Site were found during feature deactivation.", EventLogEntryType.Information);

                // Unregister Event Handler
                //Utility.UnregisterEventHandler(_pwaWeb, _webSettings.Event);
                SPSecurity.CodeToRunElevated _elevatedUnregisterEventHandler = new SPSecurity.CodeToRunElevated(UnregisterEventHandler);
                SPSecurity.RunWithElevatedPrivileges(_elevatedUnregisterEventHandler);

                // Delete Config keys
                Utility.DeleteConfigurationKeys(_pwaWeb);
            } // End try

            catch (Exception ex)
            {
                _eventLog.WriteEvent("Error encountered while deactivating TSEventHandler feature.\n\n" + ex);
                throw;
            } // End catch

            finally
            {
                _pwaWeb.Dispose();
                _eventLog.Dispose();
            } // End finally
        } // FeatureDeactivating()

        /// <summary>
        /// Occurs after a Feature is installed.
        /// </summary>
        /// <param name="properties">An 
        /// <see cref="T:Microsoft.SharePoint.SPFeatureReceiverProperties">
        /// </see> object that represents the properties of the event.</param>
        public override void FeatureInstalled(SPFeatureReceiverProperties properties)
        {
            if (!EventLog.SourceExists(_handlerEventName))
                EventLog.CreateEventSource(_handlerEventName, _eventLogName);

            if (!EventLog.SourceExists(_timerEventName))
                EventLog.CreateEventSource(_timerEventName, _eventLogName);

        } // FeatureInstalled()

        /// <summary>
        /// Occurs before a Feature is uninstalled.
        /// </summary>
        /// <param name="properties">An 
        /// <see cref="T:Microsoft.SharePoint.SPFeatureReceiverProperties">
        /// </see> object that represents the properties of the event.</param>
        public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
        {
            if (EventLog.SourceExists(_handlerEventName))
                EventLog.DeleteEventSource(_handlerEventName, ".");

            if (!EventLog.SourceExists(_timerEventName))
                EventLog.DeleteEventSource(_timerEventName, ".");
        } // FeatureUninstalling()
        #endregion // Event handlers
    }
}